#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <functional>

namespace ns_bq
{
    template <class T>
    class BlockQueue
    {
    private:
        bool IsFull()
        {
            return _block_queue.size() == _limitnums;
        }
        bool IsEmpty()
        {
            return _block_queue.size() == 0;
        }

    public:
        BlockQueue(int limitnums)
            : _limitnums(limitnums)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_productor, nullptr);
            pthread_cond_init(&_consumer, nullptr);
            _productor_wait_num = 0;
            _consumer_wait_num = 0;
        }
        void Push(const T &data)
        {
            pthread_mutex_lock(&_mutex);
            while (IsFull())
            {
                _productor_wait_num++;
                pthread_cond_wait(&_productor, &_mutex); // 1.满了就等待  2.阻塞时自动释放锁
                _productor_wait_num--;
            }
            // 资源入队列
            _block_queue.push(data);
            if (_consumer_wait_num > 0)
                pthread_cond_signal(&_consumer);
            pthread_mutex_unlock(&_mutex);
        }

        void Pop(T *data)
        {
            pthread_mutex_lock(&_mutex);
            while (IsEmpty())
            {
                _consumer_wait_num++;
                pthread_cond_wait(&_consumer, &_mutex); // 1.满了就等待  2.阻塞时自动释放锁
                _consumer_wait_num--;
            }
            // 资源入队列
            *data = _block_queue.front();
            _block_queue.pop();
            if (_productor_wait_num > 0)
                pthread_cond_signal(&_productor);
            pthread_mutex_unlock(&_mutex);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productor);
            pthread_cond_destroy(&_consumer);
        }

    private:
        std::queue<T> _block_queue;
        int _limitnums;
        pthread_mutex_t _mutex;

        pthread_cond_t _productor;
        pthread_cond_t _consumer;

        int _productor_wait_num;
        int _consumer_wait_num;
    };
}